#include<functional>
#include<iostream>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<netinet/in.h>

#include<thread>
#include<chrono>

#include<pthread.h>

using namespace std;

#define BUFF_LINE 512
#define BUFF_LINE_2 1024



typedef void(*CallbackFunc)(void);
//typedef void::UdpCs(*CallbackFunc)(void);

/***************************************
			Class Timer
***************************************/

class Timer
{
public:
	Timer();
	Timer( chrono::seconds time, CallbackFunc callbackFun);
	~Timer();

	void scheduleRunningTime();	
//private:
	chrono::seconds t;
	bool **is_running = NULL;
	CallbackFunc callbackFun = NULL;
	
};

Timer::Timer()
{
	this->t = chrono::seconds(0);
	this->is_running = NULL;
}

Timer::Timer( chrono::seconds time, CallbackFunc callbackFun)
{
	this->t = time;
	this->is_running = is_running;
	this->callbackFun = callbackFun;
}

Timer::~Timer()
{
}

void
Timer::scheduleRunningTime()
{
	this_thread::sleep_for( t );	
	this->callbackFun();
}

class TimerThread
{
public:
	TimerThread( chrono::seconds(time), CallbackFunc callbackFunc);
	TimerThread();
	~TimerThread();
	void run_independent_timer();
	void wait_for_timer();
private:
	Timer timer;
	thread *t_timer;

};

TimerThread::TimerThread( chrono::seconds(time), CallbackFunc callbackFunc )
{
	timer = Timer( time, callbackFunc);
}

TimerThread::TimerThread()
{
	timer = Timer();
}


TimerThread::~TimerThread()
{
	if(t_timer != NULL)
	{
		delete(t_timer);	
		t_timer=NULL;
	}
}

void
TimerThread::run_independent_timer()
{
	t_timer = new thread(&Timer::scheduleRunningTime, &timer );
	//t_timer->detach();
}
void
TimerThread::wait_for_timer()
{
	t_timer->join();
}


/***************************************
			Class UdpCs
***************************************/
class UdpCs
{
public:
	UdpCs();
	~UdpCs();


	/*
	client
		UdpCs udp_cs;
		udp_cs.run_sender();
	*/
	void run_sender();
	void preset_sender();
	void start_sender();
	void end_sender();

	void stop_sender();

	/*
	server
		UdpCs udp_cs;
		udp_cs.run_receiver();
	*/
	void run_receiver();
	void preset_receiver();
	void start_receiver();

	void stop_receiver();

//private:
	bool sender_running ;
	bool receiver_running ;



	int client_fd;
	int server_fd;
	struct sockaddr_in ser_addr;

	int server_port;
	int buf_send_line;
	string server_ip = "10.0.16.2";	

};


UdpCs::UdpCs()
{
	receiver_running = true;
	sender_running = true;

	server_port = 8888;
	receiver_running = true;
	sender_running = true;
}

UdpCs::~UdpCs()
{
}


void
UdpCs::stop_sender()
{
	sender_running = false;
}
void
UdpCs::stop_receiver()
{
	receiver_running = false;
}



void
UdpCs::run_sender()
{
	preset_sender();
	start_sender();
	end_sender();
	close(client_fd);
	return ;
}

void 
UdpCs::preset_sender()
{
	client_fd = socket(AF_INET, SOCK_DGRAM, 0);
	if(client_fd<0)
	{
		printf("create socket fail!");
		return;	
	}
	
	memset(&ser_addr, 0, sizeof(ser_addr));
	ser_addr.sin_family = AF_INET;
	ser_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	ser_addr.sin_port = htons(server_port);
}

void
UdpCs::start_sender()
{
	socklen_t len;
	struct sockaddr_in src;
	struct sockaddr* dst = (struct sockaddr*)&ser_addr;
	while(1)
	{
		if(sender_running==false)
		{
			//break;	
			printf("sender time out!\n");
			return ;
		}

		char buf[BUFF_LINE] = "TEST UDP MSG!\n";
		len = sizeof(*dst);
		printf("SENDER>>client:%s\n", buf);
		sendto(client_fd, buf, BUFF_LINE, 0, dst, len);
		/*
		memset(buf, 0, BUFF_LINE);
		recvfrom(client_fd, buf, BUFF_LINE, 0, (struct sockaddr*)&src, &len);
		printf("SENDER>>server:%s\n", buf);
		*/
		sleep(1);
	}
	return ;
}

void
UdpCs::end_sender()
{
	socklen_t len;
	struct sockaddr_in src;
	struct sockaddr* dst = (struct sockaddr*)&ser_addr;
	char buf[BUFF_LINE] = "END";
	len = sizeof(*dst);
	printf("SENDER>>client:%s\n", buf);
	sendto(client_fd, buf, BUFF_LINE, 0, dst, len);
	//memset(buf, 0, BUFF_LINE);
	//recvfrom(client_fd, buf, BUFF_LINE, 0, (struct sockaddr*)&src, &len);
	//printf("SENDER>>server:%s\n", buf);
	sleep(1);
}

void 
UdpCs::run_receiver()
{
	preset_receiver();
	start_receiver();
	close(server_fd);
	return ;
}

void 
UdpCs::preset_receiver()
{
	server_fd = socket(AF_INET, SOCK_DGRAM, 0);
	if(server_fd<0) 
	{
		printf("create socket fail!");	
		return ;
	}

	/*
	struct timeval timeout;
	timeout.tv_sec = 1; //seconds
	timeout.tv_usec = 0; // us
	if( setsockopt( server_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) == -1)
	{
		printf("setsockopt fail!\n");
		return ;		
	}
	*/
	

	memset(&ser_addr, 0, sizeof(ser_addr));
	ser_addr.sin_family=AF_INET;
	ser_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	ser_addr.sin_port = htons(server_port);

	int ret = bind(server_fd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
	if(ret<0)
	{
		printf("socket bind failed!");	
		return ;
	}
}

void 
UdpCs::start_receiver()
{
	char buf[BUFF_LINE_2];
	socklen_t len;

	int count;
	struct sockaddr_in client_addr;

	while(1)
	{
		if(receiver_running == false)
		{
			//break;	
			printf("receiver time out!\n");
			return ;
		}

		memset(buf, 0, BUFF_LINE_2);
		len = sizeof(client_addr);
		count = recvfrom(server_fd, buf, BUFF_LINE_2, 0, (struct sockaddr*)&client_addr, &len);
		if(count==-1)
		{
			printf("receive data fail!\n");	
			return ;
		}	
		if(strcmp(buf, "END")==0)
		{
			printf("RECEIVER>>client end, so end server...\n");
			return ;	
		}
		printf("RECEIVER>>client:%s\n", buf);
		memset(buf, 0, BUFF_LINE_2);
		sprintf(buf, "I have received %d bytes data!\n", count);
		printf("RECEIVER>>server:%s\n", buf);
		sendto(server_fd, buf, BUFF_LINE_2, 0, (struct sockaddr*)&client_addr, len);
	}
	return ;
}
/***************************************
			Class UdpController
***************************************/
class UdpController
{
public:
	UdpController(){};
	~UdpController(){};
	static void udpCs_install( UdpCs *udpCs );
	static void udpCs_stop_sender();
	static void udpCs_stop_receiver();
	
//private:
	static UdpCs *udpCs_ptr;
};

UdpCs *UdpController::udpCs_ptr = NULL;

void
UdpController::udpCs_install( UdpCs *udpCs )
{
	udpCs_ptr = udpCs ;
}
void
UdpController::udpCs_stop_sender()
{
	udpCs_ptr->stop_sender();
}
void
UdpController::udpCs_stop_receiver()
{
	udpCs_ptr->stop_receiver();
}

/***************************************
			Class UdpThread
***************************************/
class UdpThread
{
public:
	/*
	thread
		run independent sender&receiver
	*/
	UdpThread();
	~UdpThread();
	void run_independent_sender();
	void run_independent_receiver();
	void wait_for_sender();
	void wait_for_receiver();

//private:
	UdpCs udpCs;
	thread *t_receiver;
	thread *t_sender;
};


UdpThread::UdpThread()
{
}

UdpThread::~UdpThread()
{
	if(t_sender != NULL)
	{
		delete(t_sender);	
		t_sender=NULL;
	}

	if(t_receiver != NULL)
	{
		delete(t_receiver);	
		t_receiver=NULL;
	}
}

void 
UdpThread::run_independent_sender()
{
	t_sender = new thread(&UdpCs::run_sender, &udpCs);
}
void 
UdpThread::run_independent_receiver()
{
	t_receiver = new thread(&UdpCs::run_receiver, &udpCs);
}
void 
UdpThread::wait_for_sender()
{
	t_sender->join();
}
void 
UdpThread::wait_for_receiver()
{
	t_receiver->join();
}

/***************************************
		   Function Main	
***************************************/

int main(int argc, char **argv)
{

	UdpThread udpThread;

	UdpController udpController;
	udpController.udpCs_install( &udpThread.udpCs );

	TimerThread timeThread_sender(chrono::seconds(10), udpController.udpCs_stop_sender);
	TimerThread timeThread_receiver(chrono::seconds(10), udpController.udpCs_stop_receiver);

	timeThread_sender.run_independent_timer();
	timeThread_receiver.run_independent_timer();

	udpThread.run_independent_receiver();
	udpThread.run_independent_sender();


	timeThread_sender.wait_for_timer();
	timeThread_receiver.wait_for_timer();

	udpThread.wait_for_sender();
	udpThread.wait_for_receiver();

	

	return 0;
}

